package com.theaf.core.repo.specification;

import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 简单条件构造
 * @param <T>
 */
public class SqlBuilder<T> extends SqlOperator<T> {

    public SqlBuilder setRlikeSplitSymbol(String symbol){
        for(SqlOperator op:opers) {
            op.splitSymbol = symbol;
        }
        return this;
    }
    /**
     * 为所有条件设置父对象引用
     */
    @Override
    public void setParentRoot(Root parentRoot){
        if(opers!=null) {
            for (SqlOperator sqlOperator : opers) {
                sqlOperator.setParentRoot(parentRoot);
            }
        }
    }
    /**
     * 条件列表
     */
    private List<SqlOperator> opers;
    /**
     * 上层条件
     */
    private SqlBuilder parent;

    public SqlBuilder setSelects(String[] selects) {
        this.selects = selects;
        return this;
    }

    public SqlBuilder setSelect(String select) {
        this.selects = new String[]{select};
        return this;
    }

    public String[] getSelects() {
        return selects;
    }

    private String[] selects;


    public SqlBuilder() {
        opers = new ArrayList<SqlOperator>();
    }

    /**
     * 完成条件的添加
     * @return
     */
    private SqlBuilder add(Object key, SqlOperator.OPER oper, Object value, SqlOperator.JOIN join) {
        SqlOperator so = new SqlOperator();
        so.setKey(key);
        so.setValue(value);
        so.setOper(oper);
        so.setJoin(join);
        opers.add(so);
        return this;
    }

    /**
     * 添加or条件的重载
     * @return this，方便后续的链式调用
     */
    public SqlBuilder or(Object key, SqlOperator.OPER oper, Object value) {
        return this.add(key,oper,value, SqlOperator.JOIN.OR);
    }
    /**
     * 添加or条件的重载
     * @return this，方便后续的链式调用
     */
    public SqlBuilder or(Object key, Object value) {
        return this.add(key,OPER.EQ,value, SqlOperator.JOIN.OR);
    }
    /**
     * 添加or条件的重载
     * @return this，方便后续的链式调用
     */
    public SqlBuilder or(SqlBuilder expression) {
        expression.setJoin(JOIN.OR);
        opers.add(expression);
        return this;
    }

    /**
     * 条件开始
     * @param key
     * @param oper
     * @param value
     * @return
     */
    public static SqlBuilder start(Object key, SqlOperator.OPER oper, Object value){
        SqlBuilder builder = new SqlBuilder();
        SqlOperator so = new SqlOperator();
        so.setKey(key);
        so.setValue(value);
        so.setOper(oper);
        so.setJoin(JOIN.AND);
        builder.opers.add(so);
        return builder;
    }

    /**
     * 嵌套条件与开始
     * @return
     */
    public SqlBuilder nestStart(){
        SqlBuilder builder = new SqlBuilder();
        builder.setJoin(JOIN.AND);
        builder.parent = this;
        this.opers.add(builder);
        return builder;
    }

    /**
     * 嵌套条件与开始
     * @return
     */
    public SqlBuilder nestAnd(String key, SqlOperator.OPER oper, Object value){
        SqlBuilder builder = new SqlBuilder();
        SqlOperator so = new SqlOperator();
        so.setKey(key);
        so.setValue(value);
        so.setOper(oper);
        so.setJoin(JOIN.AND);
        builder.opers.add(so);
        builder.setJoin(JOIN.AND);
        builder.parent = this;
        this.opers.add(builder);
        return builder;
    }

    /**
     * 嵌套条件或开始
     * @return
     */
    public SqlBuilder nestOr(Object key, SqlOperator.OPER oper, Object value){
        SqlBuilder builder = new SqlBuilder();
        SqlOperator so = new SqlOperator();
        so.setKey(key);
        so.setValue(value);
        so.setOper(oper);
        so.setJoin(JOIN.AND);
        builder.opers.add(so);
        builder.setJoin(JOIN.OR);
        builder.parent = this;
        this.opers.add(builder);
        return builder;
    }

    /**
     * 本层条件结束，相当于}
     * @return
     */
    public SqlBuilder end(){
        return this.parent;
    }

    public static SqlBuilder start(Object key, Object value){
        SqlBuilder builder = new SqlBuilder();
        SqlOperator so = new SqlOperator();
        so.setKey(key);
        so.setValue(value);
        so.setOper(OPER.EQ);
        so.setJoin(JOIN.AND);
        builder.opers.add(so);
        return builder;
    }

    public static SqlBuilder start(SqlBuilder expression){
        SqlBuilder builder = new SqlBuilder();
        builder.opers.add(expression);
        return builder;
    }

    /**
     * 添加and的条件
     * @return
     */
    public SqlBuilder and(Object key, SqlOperator.OPER oper, Object value) {
        return this.add(key,oper,value, SqlOperator.JOIN.AND);
    }

    public SqlBuilder and(Object key, Object value) {
        return this.add(key,OPER.EQ,value, SqlOperator.JOIN.AND);
    }

    public SqlBuilder and(SqlBuilder expression) {
        expression.setJoin(JOIN.AND);
        opers.add(expression);
        return this;
    }

    public Specification gen() {
        Specification<T> specification = new Specification<T>(){
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return SqlBuilder.this.generatePredicate(root,criteriaBuilder);
            }
        };
        SqlOperator.clearRootCache();
        return specification;
    }

    @Override
    public Predicate generatePredicate(Root<T> root, CriteriaBuilder criteriaBuilder) {
        int index = 0;
        //通过resultPre来组合多个条件
        Predicate resultPre = null;
        for(SqlOperator op:opers) {
            if(index++==0) {
                resultPre = op.generatePredicate(root,criteriaBuilder);
                continue;
            }
            Predicate curent = op.generatePredicate(root,criteriaBuilder);
            if(curent==null) {
                continue;
            }
            if(resultPre==null){
                resultPre = curent;
            }else if(op.getJoin().equals(SqlOperator.JOIN.AND)) {
                resultPre = criteriaBuilder.and(resultPre,curent);
            } else if(op.getJoin().equals(SqlOperator.JOIN.OR)) {
                resultPre = criteriaBuilder.or(resultPre,curent);
            }
        }
        return resultPre;
    }
}
